package com.esd.device.utils;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.concurrent.TimeUnit;

@Component
public class Tcp_Utils_Nio {

    @Value("${esd.retryTimes}")
    private int retryTimes ;

    // 默认构造方法为重试次数为3
    public Tcp_Utils_Nio(){
        super();
    }


    public String sendCommand(String ip , int port,String msg, int timeout) throws Exception {
        String result = "";
        //1. 得到一个网络通道
        SocketChannel channel = SocketChannel.open();
        //2. 设置非阻塞方式
        channel.configureBlocking(false);
        //3. 提供服务器端的IP地址和端口号
        InetSocketAddress address = new InetSocketAddress(ip, port);
        long currentTimeMillis = System.currentTimeMillis();
        //4. 连接服务器端
        if (!channel.connect(address)) {
            while (!channel.finishConnect()) { // 循环连接
                long time = System.currentTimeMillis()-currentTimeMillis ;
                if (time >= 150){
                    channel.close();
                    return "error";
                }
            }
        }
        byte[] bytes = genRequestBytes(msg);
        for (int i = 0; i < retryTimes; i++) {
            //5. 得到一个缓冲区并存入数据
            ByteBuffer writeBuf = ByteBuffer.wrap(bytes);
            //6. 发送数据
            channel.write(writeBuf);
            TimeUnit.MILLISECONDS.sleep(timeout);
            ByteBuffer buffer = ByteBuffer.allocate(128);
            int length = channel.read(buffer);
            if (length != 0) {
                byte[] dataArr = new byte[length];
                System.arraycopy(buffer.array(), 0, dataArr, 0, length);
                result = bytes2HexString(dataArr);
            }
            if (ResultUtils.checkResult(msg, result)){
                writeBuf.clear();
                buffer.clear();
                break;
            }else {
                // 判断是否失联 或者 丢包
//                System.out.println("result is error!!");
                result = "" ;
            }
            writeBuf.clear();
            buffer.clear();
            // 正常关闭channel
        }
        channel.close();
        return result;
    }

    // 温湿度计请求
    public String sendCommand1(String ip , int port,String msg, int timeout) throws Exception {
        String result = "";
        //1. 得到一个网络通道
        SocketChannel channel = SocketChannel.open();
        //2. 设置非阻塞方式
        channel.configureBlocking(false);
        //3. 提供服务器端的IP地址和端口号
        InetSocketAddress address = new InetSocketAddress(ip, port);
        long currentTimeMillis = System.currentTimeMillis();
        //4. 连接服务器端
        if (!channel.connect(address)) {
            while (!channel.finishConnect()) { // 循环连接
                long time = System.currentTimeMillis()-currentTimeMillis ;
                if (time >= 150){
                    channel.close();
                    return "error";
                }
            }
        }
        byte[] bytes = genRequestBytes1(msg);
        for (int i = 0; i < retryTimes; i++) {
            //5. 得到一个缓冲区并存入数据
            ByteBuffer writeBuf = ByteBuffer.wrap(bytes);
            //6. 发送数据
            channel.write(writeBuf);
            TimeUnit.MILLISECONDS.sleep(timeout);
            ByteBuffer buffer = ByteBuffer.allocate(128);
            int length = channel.read(buffer);
            if (length != 0) {
                byte[] dataArr = new byte[length];
                System.arraycopy(buffer.array(), 0, dataArr, 0, length);
                result = bytes2HexString(dataArr);
                writeBuf.clear();
                buffer.clear();
                break;
            }
            writeBuf.clear();
            buffer.clear();
            // 正常关闭channel
        }
        channel.close();
        return result;
    }

    // 尘埃粒子计数器
    public String sendCommand2(String ip , int port,String msg, int timeout) throws Exception {
        String result = "";
        //1. 得到一个网络通道
        SocketChannel channel = SocketChannel.open();
        //2. 设置非阻塞方式
        channel.configureBlocking(false);
        //3. 提供服务器端的IP地址和端口号
        InetSocketAddress address = new InetSocketAddress(ip, port);
        long currentTimeMillis = System.currentTimeMillis();
        //4. 连接服务器端
        if (!channel.connect(address)) {
            while (!channel.finishConnect()) { // 循环连接
                long time = System.currentTimeMillis()-currentTimeMillis ;
                if (time >= 150){
                    channel.close();
                    return "error";
                }
            }
        }
        byte[] bytes = genRequestBytes2(msg);
        for (int i = 0; i < retryTimes; i++) {
            //5. 得到一个缓冲区并存入数据
            ByteBuffer writeBuf = ByteBuffer.wrap(bytes);
            //6. 发送数据
            channel.write(writeBuf);
            TimeUnit.MILLISECONDS.sleep(timeout);
            ByteBuffer buffer = ByteBuffer.allocate(128);
            int length = channel.read(buffer);
            if (length != 0) {
                byte[] dataArr = new byte[length];
                System.arraycopy(buffer.array(), 0, dataArr, 0, length);
                result = bytes2HexString(dataArr);
                writeBuf.clear();
                buffer.clear();
                break;
            }
            writeBuf.clear();
            buffer.clear();
            // 正常关闭channel
        }
        channel.close();
        return result;
    }

    // 将命令字符串转化为字节数组
    private byte [] genRequestBytes(String msg){
        byte[] bytes = hexString2Bytes(msg);//查询命令
        String[] ss = stringPolishing(getCRC(bytes));
        msg = msg + " " + ss[0] + " "  + ss[1];
//        System.out.println(ResultUtils.formatResult(msg));
        bytes = hexString2Bytes(msg);//再次获取带CRC的byte
        return bytes ;
    }

    private byte [] genRequestBytes1(String msg){
        String command = msg.replace(" " , "").substring(8);
        byte[] bytes = hexString2Bytes(command) ;
        String[] ss = stringPolishing(getCRC(bytes));
        msg = msg + " " + ss[1] + " "  + ss[0];
        return hexString2Bytes(msg);//再次获取带CRC是byte
    }

    private byte [] genRequestBytes2(String msg){
        byte[] bytes = hexString2Bytes(msg) ;
        String[] ss = stringPolishing(getCRC(bytes));
        msg = msg + " " + ss[1] + " "  + ss[0];
        return hexString2Bytes(msg);//再次获取带CRC是byte
    }

    // 4位CRC校验码，不足补0
    private static String[] stringPolishing(String Str) {
        int length = Str.length();
        StringBuilder StrBuilder = new StringBuilder(Str);
        for (int i = 0; i < (4 - length); i++) {
            StrBuilder.insert(0, "0");
        }
        String[] ss = new String[2];
        ss[0] = StrBuilder.substring(0, 2); // CRC 高位
        ss[1] = StrBuilder.substring(2); // CRC 低位
        return ss;
    }

    // 数学算法，获取字节数字数组的CRC校验
    private static String getCRC(byte[] bytes) {
        int CRC = 0x0000ffff;
        int POLYNOMIAL = 0x0000a001;
        int i, j;
        for (i = 0; i < bytes.length; i++) {
            CRC ^= ((int) bytes[i] & 0x000000ff);
            for (j = 0; j < 8; j++) {
                if ((CRC & 0x00000001) != 0) {
                    CRC >>= 1;
                    CRC ^= POLYNOMIAL;
                } else {
                    CRC >>= 1;
                }
            }
        }
        return Integer.toHexString(CRC);
    }

    // 从字节数组到十六进制字符串转换
    private static String bytes2HexString(byte[] bytes) {
        StringBuilder hexString = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            String hex = Integer.toHexString(bytes[i] & 0xFF); // 将 byte转换为 16进制字符串
            if (hex.length() == 1) {
                hexString.append('0');
            }
            hexString.append(hex.toUpperCase()); // 转成大写
            if (i != bytes.length - 1){
                hexString.append(" ");
            }
        }
        return hexString.toString();
    }

    private static byte[] hexString2Bytes(String hexStr) {
        hexStr = hexStr.replace(" ", "");
        if (hexStr.length() % 2 != 0){
            throw new RuntimeException("操作指令不正确！！");
        }
        byte[] b = new byte[hexStr.length() / 2];
        int j = 0;
        for (int i = 0; i < b.length; i++) {
            char c0 = hexStr.charAt(j);
            char c1 = hexStr.charAt(j+1);
            j += 2;
            b[i] = (byte) Integer.parseInt(c0 + "" + c1, 16); // 将16进制字符串转 byte
        }
        return b;
    }
}

